package com.ziytek.web.citizen.service;

import com.google.gson.Gson;
import com.ziytek.web.citizen.config.ConfigInit;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.model.bean.BatteriesDetails;
import com.ziytek.web.citizen.model.bean.StatsFirstCache;
import com.ziytek.web.citizen.model.data.RealData;
import com.ziytek.web.citizen.model.req.RealtimeDataQueryReq;
import com.ziytek.web.citizen.model.tcp.req.TcpBatteryDetailReq;
import com.ziytek.web.citizen.model.tcp.req.TcpRealTimeReq;
import com.ziytek.web.citizen.model.tcp.rsp.TcpBatteryDetailResponse;
import com.ziytek.web.citizen.model.tcp.rsp.TcpRealTimeResponse;
import com.ziytek.web.citizen.service.itf.BatteryDetailsQueryService;
import com.ziytek.web.citizen.service.itf.LastRealtimeDataQueryService;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/***
 * 给web前端推送长连接数据
 * @author fenghx
 * */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PushDataService {
    private final Gson gson;
    private final ConfigInit configInit;
    private final RealtimeDataService realtimeDataService;
    private final CabinetStatusPushService cabinetStatusPushService;
    private final BatteryDetailsQueryService batteryDetailsQueryService;
    private final LastRealtimeDataQueryService lastRealtimeDataQueryService;

    public void pushData(Object msg) {
        if (!(msg instanceof RealData rd)) {
            log.info("web客户端长连接推送数据失败,msg:{}", gson.toJson(msg));
            return;
        }

        // 第一、首先推送首界面的实时数据
        realtimeDataQueryAndPush(String.valueOf(rd.getB()));
        // 第二、查看有没有请求推送曲线数据的缓存，有者推送曲线数据
        statsQueryAndPush(String.valueOf(rd.getB()));
        // 第三、推送机柜状态信息
        //cabinetStatusPushService.cabinetStatusPush(String.valueOf(rd.getB()));
        cabinetStatusPushService.cabinetStatusPush();
    }

    public Map<String, Object> getStatsCache(String cabinetNo) {
        var key = CaffeineUtil.CACHE_KEY_TOPIC_REAL_STATS_QUERY
                + CaffeineUtil.CACHE_KEY_SYMBOL
                + Integer.parseInt(cabinetNo) + CaffeineUtil.CACHE_KEY_SYMBOL;

        Map<String, CaffeineUtil.CacheObject<?>> map = CaffeineUtil.getAllStartWith(key);
        if (CollectionUtils.isEmpty(map)) {
            return Collections.emptyMap();
        }

        List<Map<String, Object>> result = map.values().stream()
                .filter(v -> v instanceof CaffeineUtil.CacheObject<?> cacheObject
                        && cacheObject.data instanceof Map m)
                .map(v -> (Map<String, Object>) v.data)
                .toList();

        return CollectionUtils.isEmpty(result) ? Collections.emptyMap() : result.getFirst();
    }


    public void realtimeDataQueryAndPush(String cabinetNo) {
        Map<String, Object> map = realtimeDataService.getRealTimeCache(cabinetNo);
        if (CollectionUtils.isEmpty(map)) {
            log.info("首界面实时数据推送长连接缓存为空,cabinetNo:{}", cabinetNo);
            return;
        }

        var realTimeReq = (TcpRealTimeReq) map.get("req");
        var ctx = (ChannelHandlerContext) map.get("ctx");
        if (realTimeReq == null || ctx == null) {
            return;
        }

        //var door = getDoor(realTimeReq.getDoor());
        var door = realTimeReq.getDoor();
        var side = realTimeReq.getSide();

        var req = new RealtimeDataQueryReq();
        req.setDoor(door);
        req.setSide(side);
        req.setCabinetNo(cabinetNo);
        var rsp = lastRealtimeDataQueryService.queryRealData(req);

        var response = new TcpRealTimeResponse();
        response.setTopic("realTime");
        response.setCabinetNo(cabinetNo);
        response.setData(rsp.getData());

        var pushData = gson.toJson(response);
        if (configInit.getConfig().isBasicDataPrinting()) {
            log.info("首界面实时数据推送:{}", pushData);
        }
        // tcp长连接推送数据
        // ctx.writeAndFlush(Unpooled.buffer().writeBytes(pushData.getBytes(StandardCharsets.UTF_8)));
        ctx.writeAndFlush(new TextWebSocketFrame(pushData));

        var clientIp = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
        var port = ((InetSocketAddress) ctx.channel().remoteAddress()).getPort();
        putReadMap(clientIp + "_" + port, ctx);
    }


    /**
     * TCP客户端上送数据计数缓存
     */
    private void putReadMap(String clientIp, ChannelHandlerContext ctx) {
        if (ctx != null) {
            //log.info("test cl:{}",clientIp);
            CaffeineUtil.set(CaffeineUtil.WEB_IP_CACHE_PREFIX + clientIp, ctx, NumConstant.INT_SIXTY);
        }
    }

    public void statsQueryAndPush(String cabinetNo) {
        Map<String, Object> map = getStatsCache(cabinetNo);
        if (CollectionUtils.isEmpty(map)) {
            return;
        }

        var batteryDetailReq = (TcpBatteryDetailReq) map.get("req");
        var ctx = (ChannelHandlerContext) map.get("ctx");
        if (batteryDetailReq == null || ctx == null) {
            return;
        }

        var lineNo = batteryDetailReq.getLineNo();
        //var door = getDoor(batteryDetailReq.getDoor());
        var door = batteryDetailReq.getDoor();
        var channelNo = batteryDetailReq.getChannelNo();
        var timeMinute = batteryDetailReq.getTimeMinute();

        // 首次推送数据
        var key = CaffeineUtil.CACHE_KEY_TOPIC_REAL_STATS_FIRST_PUSH
                + CaffeineUtil.CACHE_KEY_SYMBOL
                + cabinetNo + CaffeineUtil.CACHE_KEY_SYMBOL
                + door + CaffeineUtil.CACHE_KEY_SYMBOL
                + lineNo + CaffeineUtil.CACHE_KEY_SYMBOL
                + channelNo;

        StatsFirstCache statsFirstCache = CaffeineUtil.get(key);
        var firstData = false;

        //log.info("曲线数据缓存：{}", statsFirstCache == null);
        if (statsFirstCache == null || statsFirstCache.getAt() < NumConstant.LONG_ZERO) {
            firstData = true;
            if (statsFirstCache == null) {
                statsFirstCache = new StatsFirstCache();
            }
            statsFirstCache.setAt(NumConstant.LONG_ZERO);
            statsFirstCache.setCtx(ctx);
            CaffeineUtil.set(key, statsFirstCache, timeMinute * 60L + NumConstant.LONG_THREE);
        }

        var type = firstData ? "all" : "increment";
        var rsp = firstData ?
                batteryDetailsQueryService.batteryDetailsQuery(cabinetNo, door, lineNo, channelNo, NumConstant.INT_ONE, NumConstant.INT_SUSPEND) :
                batteryDetailsQueryService.batteryDetailsQueryByAt(cabinetNo, door, lineNo, channelNo, statsFirstCache.getAt());

        if (rsp == null || CollectionUtils.isEmpty(rsp.getData())) {
            log.info("曲线统计数据推送,查询无数据:{}-{}-{}-{}", cabinetNo, door, lineNo, channelNo);
            return;
        }

        push(cabinetNo, door, type, lineNo, channelNo, rsp.getData(), ctx, key, timeMinute);
    }

    private void push(String cabinetNo, String door, String type, Integer lineNo, Integer channelNo, List<BatteriesDetails> data, ChannelHandlerContext ctx, String key, Long timeMinute) {
        if (CollectionUtils.isEmpty(data)) {
            return;
        }

        var response = new TcpBatteryDetailResponse();
        response.setDoor(door);
        response.setType(type);
        response.setData(data);
        response.setLineNo(lineNo);
        response.setChannelNo(channelNo);
        response.setCabinetNo(cabinetNo);
        response.setTopic("batteryDetail");

        var bs = data.getLast();
        var at = getLongTime(bs.getCt());
        var statsFirstCache = new StatsFirstCache();
        statsFirstCache.setAt(at);
        statsFirstCache.setCtx(ctx);
        CaffeineUtil.set(key, statsFirstCache, (timeMinute * 60L - at) + NumConstant.LONG_THREE);

        var pushData = gson.toJson(response);
        if (configInit.getConfig().isBasicDataPrinting()) {
            log.info("曲线统计数据推送:lastTime[{}],push:{}", bs.getCt(), pushData);
        }
        // tcp长连接推送数据
        //ctx.writeAndFlush(Unpooled.buffer().writeBytes(pushData.getBytes(StandardCharsets.UTF_8)));
        ctx.writeAndFlush(new TextWebSocketFrame(pushData));

        var clientIp = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
        var port = ((InetSocketAddress) ctx.channel().remoteAddress()).getPort();
        putReadMap(clientIp + "_" + port, ctx);

    }

    private Long getLongTime(String dateTimeStr) {
        var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        var dateTime = LocalDateTime.parse(dateTimeStr, formatter);
        return dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() + 999;
    }
}
